package cn.dengta.webapp.base.entity;

import java.util.EnumMap;
import java.util.Map;
import java.util.function.Predicate;
import javax.annotation.Nullable;

import cn.dengta.context.model.Language;
import cn.dengta.context.model.WesKit;
import com.alibaba.fastjson.JSONObject;
import lombok.*;
import me.codeplayer.util.*;
import org.apache.commons.lang3.StringUtils;

/**
 * 支持国际化的多语言数据（支持嵌套多种数据结构）
 */
@Getter
@Setter
public class I18nData<T> implements I18nSupport {

	public static boolean I18N_ENABLED = true;

	/** 基于本地化的内容 */
	@Getter(AccessLevel.NONE)
	protected T data;
	/** 基于国际化的内容Map */
	protected Map<Language, T> dataMap;

	@Override
	public void validate(boolean allowSkip) {
		Assert.isTrue(allowSkip || checkValid(), "多语言配置不正确，请核实！");
	}

	public T get() {
		return I18N_ENABLED && X.isValid(dataMap) ? get(Language.getCurrentReqLanguage()) : data;
	}

	public T get(Language lang) {
		final Map<Language, T> map = this.dataMap;
		if (map == null || map.isEmpty()) {
			return data;
		}
		T text = map.get(lang);
		if (text == null && lang != Language.DEFAULT) {
			text = map.get(Language.DEFAULT);
		}
		return text == null ? data : text;
	}

	public String serialize() {
		if (I18N_ENABLED && X.isValid(dataMap)) {
			return JSONUtil.encode(dataMap);
		}
		return StringUtil.toString(data);
	}

	public static String serialize(@Nullable I18nData<?> content) {
		if (content == null) {
			return "";
		}
		return content.serialize();
	}

	public static <T> I18nData<T> deserialize(String text, Class<T> type) {
		final I18nData<T> t = new I18nData<>();
		initWith(text, type, t);
		return t;
	}

	@SuppressWarnings("unchecked")
	protected static <T> void initWith(String text, Class<T> type, I18nData<T> t) {
		if (I18N_ENABLED) {
			// 兼容空文本
			if (StringUtil.isEmpty(text)) {
				return;
			}
			// 兼容非多语言的纯文本内容
			if (!StringUtil.startsWith(text, '{')) {
				t.data = (T) text;
				return;
			}
			// 兼容旧版JSON格式：{"nameEntries":{"zh_CN":{"name":"Flala起航"},"zh":{"name":"Flala起航"},"en":{"name":"Flala sets sail"},"vi":{"name":"Flala ra khơi"}}}
			String prefix = "{\"nameEntries\":";
			if (StringUtils.startsWith(text, prefix)) {
				text = text.substring(prefix.length(), text.length() - 1);
				JSONObject json = JSONObject.parseObject(text);
				final EnumMap<Language, T> map = new EnumMap<>(Language.class);
				for (Map.Entry<String, Object> entry : json.entrySet()) {
					final Language lang = Language.valueOf(entry.getKey());
					final JSONObject nameEntry = (JSONObject) entry.getValue();
					map.put(lang, (T) nameEntry.getString("name"));
				}
				t.dataMap = map;
				return;
			}
			// 兼容旧版JSON格式：{"dataEntries":{"ja_JP":"貴方は結婚しない派ですか？","ko_KR":"당신은 비혼을 받아들이십니까?","th_TH":"คุณเป็นคนไม่แต่งงานไหม","en":"Are you an unmarried people?","zh_CN":"你是个不婚族吗？","vi":"Bạn có phải là người không muốn kết hôn?","zh":"你是個不婚族嗎？","in_ID":"Apakah Anda seseorang yang tidak menikah?"}}
			// 兼容旧版JSON格式：{"dataEntries":{"ja_JP":["好き"],"ko_KR":["좋아하다"],"th_TH":["ชอบ"],"en":["Like"],"zh_CN":["喜欢"],"vi":["Thích"],"zh":["喜歡"],"in_ID":["Suka"]}}
			prefix = "{\"dataEntries\":";
			if (StringUtils.startsWith(text, prefix)) {
				text = text.substring(prefix.length(), text.length() - 1);
			}
			t.dataMap = JSONUtil.parseObject(text, EmbedLanguageDataMap.class);
		} else {
			t.data = type == String.class ? (T) text : JSONUtil.parseObject(text, type);
		}
	}

	public boolean checkValid() {
		return checkValid(StringUtil::notEmpty);
	}

	public boolean checkValid(Predicate<? super T> validator) {
		if (I18N_ENABLED) {
			final Map<Language, T> map = this.dataMap;
			if (map != null && map.size() >= Language.CACHE.length) {
				for (Language lang : Language.CACHE) {
					if (!validator.test(map.get(lang))) {
						return false;
					}
				}
				return true;
			}
			return false;
		} else {
			return validator.test(data);
		}
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static class EmbedLanguageDataMap extends EnumMap {

		public EmbedLanguageDataMap() {
			super(Language.class);
		}

		@Override
		public Object put(Object key, Object value) {
			if (key instanceof String) {
				key = Language.valueOf(key.toString());
			}
			return super.put((Language) key, value);
		}

	}

}
